在 Day 03 - 由上而下投資法:先看大盤趨勢再選股 的內容中,我們曾經提到 系統性風險 是 不可分散風險,無法靠分散投資來消除,但是透過 期貨 與 選擇權 等衍生性金融商品則可以 避險。舉例來說,如果我們擁有股票的現貨部位,當遇到系統風險時,因「覆巢之下無完卵」,即使是體質好的公司,股價也會被向下修正。如果我們在股市下跌前已經先「賣出臺股期貨」建立避險的空方部位,就可以透過期貨部位的獲利來抵消現貨部位的損失,達到消除系統風險的效果。
臺灣期貨與選擇權市場也有三大法人,即 外資、投信 與 自營商。先前我們在 Day 04 - 法人主導的市場:三大法人買賣超 的內容已經說明,外資的買賣方向對於臺股有舉足輕重的影響力,因此三大法人在期貨與選擇權市場的留倉部位,外資也是我們主要的觀察對象。因期貨交易的本質是預期商品未來的價格,而法人在股票市場持有現貨部位,所以在期貨市場主要目的是避險,故外資在臺股期貨的佈局,也就透露出外資對臺股後市的看法。
期貨(Futures)是一種衍生性金融商品,期貨契約是買賣雙方約定於未來某一特定時點,以交易當時約定之價格交付某特定商品的一種標準化合約。期貨的英文名稱是未來,意即期貨的交易的本質就是一種買賣預期未來的交易行為。期貨契約會連結一個現貨標的,臺灣期貨交易所的「臺股期貨」商品,交易標的就是「臺灣證券交易所發行量加權股價指數」。
在股票市場,交易單位通常是 1 股或 1 張(1000 股),因股票發行的數量是固定的,所以實際流通在外股數會影響成交量的大小。期貨則是契約,計算的單位是「口」,每口契約都涉及一個買方與賣方,只要期貨市場上有買方與賣方,就可以共同創造一口契約,所以沒有籌碼流通的限制。
在期貨市場中,倉位(Position)代表持有期貨契約的部位,以下是期貨市場中常用的術語:
所謂 未平倉(Open Interest)就是指契約到期日前,尚未結束契約的部位數量,也稱為 留倉。需要注意的是,未平倉量代表多方部位或空方部位還沒有平倉的單邊總數量,並不是兩邊的總和,所以:
總未平倉量 = 多方未平倉量 = 空方未平倉量
在三大法人中,外資對股市現貨行情的主導性最強,所以我們也會同時關注外資在期貨市場的佈局。未平倉淨口數,也稱為 淨未平倉口數、淨部位,計算方式是未平倉多方口數減去未平倉空方口數,即:
未平倉淨口數 = 未平倉多方口數 - 未平倉空方口數
當未平倉淨口數為正數,代表留倉部位是 淨多單;當未平倉淨口數為負數,代表留倉部位是 淨空單。因此,當外資在臺股期貨的部位是淨多單時,表示比較看多臺股後市;當外資在臺股期貨的部位是淨空單時,表示比較看空臺股後市。
下圖表示 2022 年初至 8 月 31 日的加權指數與外資臺股期貨淨口數的關係。我們可以發現,在 2022 年初加權指數仍在 18,000 點時,外資留有 30,000 口以上的避險空單,隨著行情下跌,外資空單也一路回補。
Source:臺灣證券交易所、臺灣期貨交易所
除了觀察外資在臺股期貨的未平倉淨口數,還可以注意外資在臺股期貨的每日 未平倉淨口數增減。如果未平倉淨口數增加,代表多單增加,當日籌碼面偏多看待;如果未平倉淨口數減少,代表空單增加,當日籌碼面偏空看待。
提供一個有趣的數據,下表是政府宣布第 8 次國安基金進場(2022 年 7 月 12 日)前後三天的外資臺股期貨淨未平倉數據:
日期 | 加權指數 | 漲跌 | 漲跌幅 | 外資買賣超(億) | 外資臺股期貨淨未平倉(口) | 淨未平倉增減(口) |
---|---|---|---|---|---|---|
2022-07-15 | 14550.62 | 112.10 | 0.78% | -111.62 | -568 | -111 |
2022-07-14 | 14438.52 | 113.84 | 0.79% | 10.84 | -457 | -3,930 |
2022-07-13 | 14324.68 | 374.06 | 2.68% | 57.87 | 3,473 | -4,061 |
2022-07-12 | 13950.62 | -389.91 | -2.72% | -96.85 | 7,534 | 4,800 |
2022-07-11 | 14340.53 | -124.00 | -0.86% | -30.60 | 2,734 | 2,891 |
2022-07-08 | 14464.53 | 128.26 | 0.89% | -39.36 | -157 | 6,391 |
2022-07-07 | 14336.27 | 350.76 | 2.51% | 159.82 | -6,548 | 2,046 |
Source:臺灣證券交易所、臺灣期貨交易所
在 2022 年 7 月 11 日這天,外資臺股期貨部位已經轉為淨多單。7 月 12 日加權指數大跌 -389.91 點,指數摜破萬四關卡,外資卻在臺股期貨逆勢加碼 4,800 口多單,當日傍晚國安基金發布新聞稿決定啟動安定市場任務。隔日受護盤消息激勵,7 月 12 日加權指數大漲 374.06 點,外資臺股期貨多單也適時獲利了結。此舉可以說是外資「神操作」,也有人認為「凡事有人先知道」,這是市場有趣的地方,就看人們怎麼解讀囉!
在期交所網站的 交易資訊-三大法人-查詢-區分各期貨契約-依日期 頁面,可以按日查詢三大法人在各期貨契約的交易資訊。
期交所首頁 > 交易資訊 > 三大法人 > 查詢 > 區分各期貨契約 > 依日期
在「交易資訊-三大法人-查詢-區分各期貨契約-依日期」頁面,選取要查詢的「日期」,「契約」選擇「臺股期貨」送出查詢後,就會列出該日三大法人在臺股期貨的交易資訊。
我們可以找到外資在臺股期貨的交易口數與契約金額,其中「未平倉餘額」的「多空淨額口數」,就是「未平倉淨口數」。
在期交所網站的 交易資訊-三大法人-下載-區分各期貨契約-依日期 頁面,可以按日期範圍下載三大法人在各期貨契約的交易資訊,期交所以 CSV 檔案格式提供。
期交所首頁 > 交易資訊 > 三大法人 > 下載 > 區分各期貨契約 > 依日期
在「交易資訊-三大法人-下載-區分各期貨契約-依日期」頁面,選取「日期(起)」、「日期(迄)」的日期範圍,「契約」選擇「臺股期貨」後,然後點擊下載,就可以取得查詢結果的 CSV 檔案。實際上,這個 HTML Form 表單是使用 POST 方法,向以下位址提交表單請求:
https://www.taifex.com.tw/cht/3/futContractsDateDown
這個表單可設定的欄位如下:
queryStartDate
:日期(起)。接受的日期格式為 yyyy/MM/dd
,如 2022/07/01
。queryEndDate
:日期(迄)。接受的日期格式為 yyyy/MM/dd
,如 2022/07/01
。commodityId
:商品契約代號。臺股期貨為 TXF
。我們可以打開終端機使用 curl
指令模擬表單請求:
$ curl --request POST \
--url https://www.taifex.com.tw/cht/3/futContractsDateDown \
--header 'Content-Type: multipart/form-data' \
--form queryStartDate=2022/07/01 \
--form queryEndDate=2022/07/01 \
--form commodityId=TXF
瞭解下載 CSV 檔案的方式後,我們就可以實作程式取得資料。
以上透過
curl
指令取得的資料在中文部分可能會呈現亂碼,不過別擔心,我們在實作程式時會處理編碼問題。
在期交所取得的盤後資訊大都是 CSV 檔案,因此我們先安裝 csvtojson
套件,方便將 CSV 格式資料轉為 JSON 格式。
$ npm install --save csvtojson
套件安裝完成後,我們新增一個 TaifexScraperService
表示從期交所取得資料的服務。打開終端機使用 Nest CLI 建立 TaifexScraperService
:
$ nest g service scraper/taifex-scraper --flat --no-spec
Nest CLI 會在 src/scraper
目錄下建立 taifex-scraper.service.ts
檔案,並且將 TaifexScraperService
加入至 ScraperModule
的 providers
設定。
開啟 src/scraper/taifex-scraper.service.ts
檔案,在 TaifexScraperService
實作 fetchInstInvestorsTxfTrades()
方法,取得三大法人臺股期貨交易資訊:
import * as csvtojson from 'csvtojson';
import * as iconv from 'iconv-lite';
import * as numeral from 'numeral';
import { DateTime } from 'luxon';
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';
@Injectable()
export class TaifexScraperService {
constructor(private httpService: HttpService) {}
async fetchInstInvestorsTxfTrades(date: string) {
// 將 `date` 轉換成 `yyyy/MM/dd` 格式
const queryDate = DateTime.fromISO(date).toFormat('yyyy/MM/dd');
// 建立 FormData
const form = new URLSearchParams({
queryStartDate: queryDate, // 日期(起)
queryEndDate: queryDate, // 日期(迄)
commodityId: 'TXF', // 契約-臺股期貨
});
const url = 'https://www.taifex.com.tw/cht/3/futContractsDateDown';
// 取得回應資料並將 CSV 轉換成 JSON 格式及正確編碼
const responseData = await firstValueFrom(this.httpService.post(url, form, { responseType: 'arraybuffer' }))
.then(response => csvtojson({ noheader: true, output: 'csv' }).fromString(iconv.decode(response.data, 'big5')));
// 若該日期非交易日或尚無資料則回傳 null
const [ fields, dealers, sitc, fini ] = responseData;
if (fields[0] !== '日期') return null;
// 合併三大法人交易數據並將 string 型別數字轉換成 number
const raw = [ ...dealers.slice(3), ...sitc.slice(3), ...fini.slice(3) ]
.map(data => numeral(data).value());
const [
dealersLongTradeVolume, // 自營商-多方交易口數
dealersLongTradeValue, // 自營商-多方交易契約金額(千元)
dealersShortTradeVolume, // 自營商-空方交易口數
dealersShortTradeValue, // 自營商-空方交易契約金額(千元)
dealersNetTradeVolume, // 自營商-多空交易口數淨額
dealersNetTradeValue, // 自營商-多空交易契約金額淨額(千元)
dealersLongOiVolume, // 自營商-多方未平倉口數
dealersLongOiValue, // 自營商-多方未平倉契約金額(千元)
dealersShortOiVolume, // 自營商-空方未平倉口數
dealersShortOiValue, // 自營商-空方未平倉契約金額(千元)
dealersNetOiVolume, // 自營商-多空未平倉口數淨額
dealersNetOiValue, // 自營商-多空未平倉契約金額淨額(千元)
sitcLongTradeVolume, // 投信-多方交易口數
sitcLongTradeValue, // 投信-多方交易契約金額(千元)
sitcShortTradeVolume, // 投信-空方交易口數
sitcShortTradeValue, // 投信-空方交易契約金額(千元)
sitcNetTradeVolume, // 投信-多空交易口數淨額
sitcNetTradeValue, // 投信-多空交易契約金額淨額(千元)
sitcLongOiVolume, // 投信-多方未平倉口數
sitcLongOiValue, // 投信-多方未平倉契約金額(千元)
sitcShortOiVolume, // 投信-空方未平倉口數
sitcShortOiValue, // 投信-空方未平倉契約金額(千元)
sitcNetOiVolume, // 投信-多空未平倉口數淨額
sitcNetOiValue, // 投信-多空未平倉契約金額淨額(千元)
finiLongTradeVolume, // 外資-多方交易口數
finiLongTradeValue, // 外資-多方交易契約金額(千元)
finiShortTradeVolume, // 外資-空方交易口數
finiShortTradeValue, // 外資-空方交易契約金額(千元)
finiNetTradeVolume, // 外資-多空交易口數淨額
finiNetTradeValue, // 外資-多空交易契約金額淨額(千元)
finiLongOiVolume, // 外資-多方未平倉口數
finiLongOiValue, // 外資-多方未平倉契約金額(千元)
finiShortOiVolume, // 外資-空方未平倉口數
finiShortOiValue, // 外資-空方未平倉契約金額(千元)
finiNetOiVolume, // 外資-多空未平倉口數淨額
finiNetOiValue, // 外資-多空未平倉契約金額淨額(千元)
] = raw;
return {
date,
finiLongTradeVolume,
finiLongTradeValue,
finiShortTradeVolume,
finiShortTradeValue,
finiNetTradeVolume,
finiNetTradeValue,
finiLongOiVolume,
finiLongOiValue,
finiShortOiVolume,
finiShortOiValue,
finiNetOiVolume,
finiNetOiValue,
sitcLongTradeVolume,
sitcLongTradeValue,
sitcShortTradeVolume,
sitcShortTradeValue,
sitcNetTradeVolume,
sitcNetTradeValue,
sitcLongOiVolume,
sitcLongOiValue,
sitcShortOiVolume,
sitcShortOiValue,
sitcNetOiVolume,
sitcNetOiValue,
dealersLongTradeVolume,
dealersLongTradeValue,
dealersShortTradeVolume,
dealersShortTradeValue,
dealersNetTradeVolume,
dealersNetTradeValue,
dealersLongOiVolume,
dealersLongOiValue,
dealersShortOiVolume,
dealersShortOiValue,
dealersNetOiVolume,
dealersNetOiValue,
};
}
}
在 fetchInstInvestorsTxfTrades()
方法中,需要指定 date
參數,表示要取得三大法人臺股期貨交易資訊的日期。我們定義回傳的物件欄位包含如下:
date
:日期finiLongTradeVolume
:外資-多方交易口數finiLongTradeValue
:外資-多方交易契約金額(千元)finiShortTradeVolume
:外資-空方交易口數finiShortTradeValue
:外資-空方交易契約金額(千元)finiNetTradeVolume
:外資-多空交易口數淨額finiNetTradeValue
:外資-多空交易契約金額淨額(千元)finiLongOiVolume
:外資-多方未平倉口數finiLongOiValue
:外資-多方未平倉契約金額(千元)finiShortOiVolume
:外資-空方未平倉口數finiShortOiValue
:外資-空方未平倉契約金額(千元)finiNetOiVolume
:外資-多空未平倉口數淨額finiNetOiValue
:外資-多空未平倉契約金額淨額(千元)sitcLongTradeVolume
:投信-多方交易口數sitcLongTradeValue
:投信-多方交易契約金額(千元)sitcShortTradeVolume
:投信-空方交易口數sitcShortTradeValue
:投信-空方交易契約金額(千元)sitcNetTradeVolume
:投信-多空交易口數淨額sitcNetTradeValue
:投信-多空交易契約金額淨額(千元)sitcLongOiVolume
:投信-多方未平倉口數sitcLongOiValue
:投信-多方未平倉契約金額(千元)sitcShortOiVolume
:投信-空方未平倉口數sitcShortOiValue
:投信-空方未平倉契約金額(千元)sitcNetOiVolume
:投信-多空未平倉口數淨額sitcNetOiValue
:投信-多空未平倉契約金額淨額(千元)dealersLongTradeVolume
:自營商-多方交易口數dealersLongTradeValue
:自營商-多方交易契約金額(千元)dealersShortTradeVolume
:自營商-空方交易口數dealersShortTradeValue
:自營商-空方交易契約金額(千元)dealersNetTradeVolume
:自營商-多空交易口數淨額dealersNetTradeValue
:自營商-多空交易契約金額淨額(千元)dealersLongOiVolume
:自營商-多方未平倉口數dealersLongOiValue
:自營商-多方未平倉契約金額(千元)dealersShortOiVolume
:自營商-空方未平倉口數dealersShortOiValue
:自營商-空方未平倉契約金額(千元)dealersNetOiVolume
:自營商-多空未平倉口數淨額dealersNetOiValue
:自營商-多空未平倉契約金額淨額(千元)完成後,我們只要呼叫 TaifexScraperService
的 fetchInstInvestorsTxfTrades()
方法,就可以按日期取得三大法人臺股期貨交易資訊。以日期 2022-07-01
為例:
{
date: '2022-07-01',
finiLongTradeVolume: 130389,
finiLongTradeValue: 376680348,
finiShortTradeVolume: 127583,
finiShortTradeValue: 368923214,
finiNetTradeVolume: 2806,
finiNetTradeValue: 7757134,
finiLongOiVolume: 35244,
finiLongOiValue: 99897182,
finiShortOiVolume: 50537,
finiShortOiValue: 143119489,
finiNetOiVolume: -15293,
finiNetOiValue: -43222307,
sitcLongTradeVolume: 536,
sitcLongTradeValue: 1518576,
sitcShortTradeVolume: 2468,
sitcShortTradeValue: 6985785,
sitcNetTradeVolume: -1932,
sitcNetTradeValue: -5467209,
sitcLongOiVolume: 24963,
sitcLongOiValue: 70770105,
sitcShortOiVolume: 26366,
sitcShortOiValue: 74747610,
sitcNetOiVolume: -1403,
sitcNetOiValue: -3977505,
dealersLongTradeVolume: 16391,
dealersLongTradeValue: 47197126,
dealersShortTradeVolume: 17189,
dealersShortTradeValue: 49499983,
dealersNetTradeVolume: -798,
dealersNetTradeValue: -2302857,
dealersLongOiVolume: 11621,
dealersLongOiValue: 32791290,
dealersShortOiVolume: 7373,
dealersShortOiValue: 20772017,
dealersNetOiVolume: 4248,
dealersNetOiValue: 12019273
}
本系列文已正式出版為《Node.js 量化投資全攻略:從資料收集到自動化交易系統建構實戰》。本書新增了全新內容和實用範例,為你提供更深入的學習體驗!歡迎參考選購,開始你的量化投資之旅!
天瓏網路書店連結:https://www.tenlong.com.tw/products/9786263336070